package com.bkfm.framework.core.shiro;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.bkfm.framework.core.constants.BkfmConstants;
import com.bkfm.framework.sys.api.service.IBkfmUserService;
import com.bkfm.framework.sys.entity.BkfmUsers;




/**
 * @author chenlf
 *
 *         2014-3-24
 */
public class ShiroRealm extends AuthorizingRealm {

	private final static Logger logger = LoggerFactory.getLogger(ShiroRealm.class);

	public IBkfmUserService service;


	public IBkfmUserService getService() {
		return service;
	}

	public void setService(IBkfmUserService service) {
		this.service = service;
	}

	/**
	 * 为当前登录的Subject授予角色和权限
	 *
	 * @see 经测试:本例中该方法的调用时机为需授权资源被访问时
	 * @see 经测试:并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache
	 * @see 个人感觉若使用了Spring3
	 *      .1开始提供的ConcurrentMapCache支持,则可灵活决定是否启用AuthorizationCache
	 * @see 比如说这里从数据库获取权限信息时,先去访问Spring3.1提供的缓存,而不使用Shior提供的AuthorizationCache
	 */
	/**
	 * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		// 获取当前登录的用户名,等价于(String)principals.fromRealm(this.getName()).iterator().next()
		String username = (String) super.getAvailablePrincipal(principals);
		// List<String> roleList = new ArrayList<String>();
		// List<String> permissionList = new ArrayList<String>();
		// //从数据库中获取当前登录用户的详细信息
		// User user = userService.queryEditorUser(sysUser);
		// if(null != user){
		// //实体类User中包含有用户角色的实体类信息
		// if(null!=user.getRoles() && user.getRoles().size()>0){
		// //获取当前登录用户的角色
		// for(Role role : user.getRoles()){
		// roleList.add(role.getName());
		// //实体类Role中包含有角色权限的实体类信息
		// if(null!=role.getPermissions() && role.getPermissions().size()>0){
		// //获取权限
		// for(Permission pmss : role.getPermissions()){
		// if(!StringUtils.isEmpty(pmss.getPermission())){
		// permissionList.add(pmss.getPermission());
		// }
		// }
		// }
		// }
		// }
		// }else{
		// throw new AuthorizationException();
		// }
		// //为当前用户设置角色和权限
		// SimpleAuthorizationInfo simpleAuthorInfo = new
		// SimpleAuthorizationInfo();
		// simpleAuthorInfo.addRoles(roleList);
		// simpleAuthorInfo.addStringPermissions(permissionList);
		SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();

		logger.debug("username = "+username);

		// 实际中可能会像上面注释的那样从数据库取得
		if (null != username) {
			//获取用户菜单信息
			//List<Menu> userMenu = userService.queryUserMenu(username);
			//setSession("usermenu", userMenu);
			// 添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色
//			simpleAuthorInfo.addRole("admin");
			// 添加权限
//			simpleAuthorInfo.addStringPermission("admin:manage");
//			System.out.println("已为用户[mike]赋予了[admin]角色和[admin:manage]权限");
			return simpleAuthorInfo;
		}
		return null;
	}

	/**
	 * 验证当前登录的Subject
	 *
	 * @see 经测试:本例中该方法的调用时机为LoginController.login()方法中执行Subject.login()时
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		// 获取基于用户名和密码的令牌
		// 实际上这个authcToken是从LoginController里面currentUser.login(token)传过来的
		// 两个token的引用都是一样的
		UsernamePasswordToken token = (UsernamePasswordToken)authcToken;
		String username = token.getUsername();
		String password = String.valueOf(token.getPassword());
		BkfmUsers loginUser = new BkfmUsers();
		loginUser.setUser_no(username);
		loginUser.setUser_pwd(password);


		try{
			BkfmUsers users = service.login(loginUser);
			if(StringUtils.isEmpty(users)){
				throw new Exception("用户名或者密码错误");
			}else{
				AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(username, users.getUser_pwd(), getName());
				setSession(BkfmConstants.CURRENT_USER, users);
				return authcInfo;
			}
		}catch(Exception e){
			logger.error("单点登录异常",e);
		}

		return null;
	}

	/**
	 * 将一些数据放到ShiroSession中,以便于其它地方使用
	 *
	 * @see 比如Controller,使用时直接用HttpSession.getAttribute(key)就可以取到
	 */
	private void setSession(Object key, Object value) {
		Subject currentUser = SecurityUtils.getSubject();
		if (null != currentUser) {
			Session session = currentUser.getSession();
			logger.debug("Session默认超时时间为[" + session.getTimeout() + "]毫秒");
			if (null != session) {
				session.setAttribute(key, value);
			}
		}
	}

	/**
	 * 获取session值
	 * @方法没描述：
	 * @author XQ
	 * @param key
	 * @return
	 */
	private Object getSession(Object key){
		Subject currentUser = SecurityUtils.getSubject();
		if (null != currentUser) {
			Session session = currentUser.getSession();
			if (null != session) {
				return session.getAttribute(key);
			}
		}
		return null;
	}
}
